/******************************************************************************
 Copyright (c) 2020-2025 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

******************************************************************************/

#include "csr_synergy.h"

#include "csr_bt_cm_main.h"
#include "csr_bt_cm_dm.h"
#include "csr_log_text_2.h"
#include "csr_bt_cm_le.h"

#ifdef CSR_BT_INSTALL_EXTENDED_ADVERTISING

void CsrBtCmDmExtAdvRegisterAppAdvSetReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvRegisterAppAdvSetReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;

    dm_ext_adv_register_app_adv_set_req(prim->advHandle, prim->flags, NULL);
}

void CsrBtCmDmExtAdvUnregisterAppAdvSetReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvUnregisterAppAdvSetReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

    dm_ext_adv_unregister_app_adv_set_req(prim->advHandle, NULL);
}

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
void CmDmExtAdvEnableReqCleanup(cmInstanceData_t* cmData, CsrUint8 advHandle)
{
    /* Adv set params are cleared */
    cmData->extAdvSet[advHandle].timerId = CSR_SCHED_TID_INVALID;
    cmData->extAdvSet[advHandle].state = CM_RPA_STATE_NONE;
    cmData->extAdvSet[advHandle].abort = FALSE;
    cmData->extAdvSet[advHandle].isMultiEnable = FALSE;
    cmData->extAdvSet[advHandle].duration = 0x00;

    SetBdAddrInvalid(&(cmData->extAdvSet[advHandle].random_addr));
}
void CmDmExtAdvEnableReqInitiateAbort(cmInstanceData_t* cmData, CsrUint8 advHandle)
{
    /* Bit 0 is set for enabling RPA generation per advertisement set feature.*/
    if (advHandle < MAX_EXT_ADV_APP &&
       (cmData->extAdvSet[advHandle].extAdvFeatureMask & CM_RPA_GEN_PER_ADV_SET_MASK) == CM_RPA_GEN_PER_ADV_SET_MASK)
    {
        CsrSchedTimerCancel(cmData->extAdvSet[advHandle].timerId, NULL, NULL);

        /* If disable from timeout process was queued earlier, that message is cleared from the DM queue.
             and the adv set params are cleared.*/
        if (cmData->extAdvSet[advHandle].state == CM_RPA_STATE_EXT_ADV_DISABLE)
        {
            CmDmQueueRemoveMsg(cmData,
                              CSR_BT_CM_EXT_ADV_ENABLE_REQ,
                              &advHandle,
                              NULL);
            CmDmExtAdvEnableReqCleanup(cmData, advHandle);
        }
        /* If any other process apart from disable , initiated from CM, was running, it is stopped */
        else if (cmData->extAdvSet[advHandle].state > CM_RPA_STATE_EXT_ADV_DISABLE)
        {
            cmData->extAdvSet[advHandle].abort = TRUE;
        }
        /* If state is none then adv set params are cleared.*/
        else
        {
            CmDmExtAdvEnableReqCleanup(cmData, advHandle);
        }
        CSR_LOG_TEXT_INFO((CsrBtCmLto, 0, "Abort initiated for adv set: 0x%x", advHandle));
    }
}
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */

void CsrBtCmDmExtAdvSetParamsReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvSetParamsReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
    if (prim->advHandle < MAX_EXT_ADV_APP)
    {
        /* Initiated abort to cleanup the RPA gen per adv set feature related parameters in case feature has 
           been disabled after it was enabled previously.*/
        CmDmExtAdvEnableReqInitiateAbort(cmData, prim->advHandle);

        /* Store the feature mask values indicating if specific features are enabled for a given ADV set.*/
        cmData->extAdvSet[prim->advHandle].extAdvFeatureMask = prim->featureMask;
        cmData->extAdvSet[prim->advHandle].isMultiEnable = FALSE;
        cmData->extAdvSet[prim->advHandle].duration = 0x00;
    }
    else
    {
        CsrGeneralException(CsrBtCmLto,
                            0,
                            CSR_BT_CM_PRIM,
                            CSR_BT_CM_EXT_ADV_SET_PARAMS_REQ,
                            0,
                            "CsrBtCmDmExtAdvSetParamsReqHandler invalid adv handle\n");
    }
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */

    dm_ext_adv_set_params_req(prim->advHandle,
                            prim->advEventProperties,
                            prim->primaryAdvIntervalMin,
                            prim->primaryAdvIntervalMax,
                            prim->primaryAdvChannelMap,
                            prim->ownAddrType,
                            &prim->peerAddr,
                            prim->advFilterPolicy,
                            prim->primaryAdvPhy,
                            prim->secondaryAdvMaxSkip,
                            prim->secondaryAdvPhy,
                            prim->advSid,
                            0,
                            NULL);
}

void CsrBtCmDmExtAdvSetDataReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvSetDataReq *prim = cmData->recvMsgP;
    uint8_t *data = NULL;

    if (prim->dataLength)
    {
        uint8_t index, offset, length;
        data = CsrPmemAlloc(prim->dataLength);

        for(offset = 0, index = 0; offset < prim->dataLength;
                               index++, offset += length)
        {
            length = prim->dataLength - offset;
            if(length > CM_EXT_ADV_DATA_BLOCK_SIZE)
                length = CM_EXT_ADV_DATA_BLOCK_SIZE;

            memcpy(&data[offset], prim->data[index], length);
        }
    }

    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

    dm_hci_ext_adv_set_data_req(prim->advHandle,
                            prim->operation,
                            prim->fragPreference,
                            prim->dataLength,
                            data,
                            NULL);

    if (data)
        CsrPmemFree(data);
}

void CsrBtCmDmExtAdvSetScanRespDataReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvSetScanRespDataReq *prim = cmData->recvMsgP;
    uint8_t *data = NULL;

    if (prim->dataLength)
    {
        uint8_t index, offset, length;
        data = CsrPmemAlloc(prim->dataLength);

        for(offset = 0, index = 0; offset < prim->dataLength;
                               index++, offset += length)
        {
            length = prim->dataLength - offset;
            if(length > CM_EXT_ADV_SCAN_RESP_DATA_BLOCK_SIZE)
                length = CM_EXT_ADV_SCAN_RESP_DATA_BLOCK_SIZE;

            memcpy(&data[offset], prim->data[index], length);
        }
    }

    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

    dm_hci_ext_adv_set_scan_resp_data_req(prim->advHandle,
                            prim->operation,
                            prim->fragPreference,
                            prim->dataLength,
                            data,
                            NULL);

    if (data)
        CsrPmemFree(data);
}

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
void CmDmExtAdvEnableReqTimeout(CsrUint16 mi, void* data)
{
    CsrUint8 advHandle = (CsrUint8)mi;
    cmInstanceData_t* cmData = (cmInstanceData_t*)data;

    /* In case of multi adv enable, if duration is non-zero.*/
    if (cmData->extAdvSet[advHandle].isMultiEnable && cmData->extAdvSet[advHandle].duration != 0)
    {
        /* Duration of adv remaining after timeout.*/
        if (cmData->extAdvSet[advHandle].duration >= cmData->extAdvSet[advHandle].timeout)
        {
            cmData->extAdvSet[advHandle].duration -= cmData->extAdvSet[advHandle].timeout;
        }
        /* Duration has expired, abort the adv process for this adv set.*/
        if (cmData->extAdvSet[advHandle].duration == 0)
        {
            cmData->extAdvSet[advHandle].state = CM_RPA_STATE_EXT_ADV_MULTI_DISABLE;
            CmExtAdvEnableReqSend(CSR_BT_CM_IFACEQUEUE,
                                  advHandle,
                                  0x00);
            return;
        }
        /* Adv is continued for remaining duration after timeout.*/
        else if (cmData->extAdvSet[advHandle].duration < cmData->extAdvSet[advHandle].timeout)
        {
            cmData->extAdvSet[advHandle].timeout = (CsrUint32)(cmData->extAdvSet[advHandle].duration);
        }
    }
    /* Timer is reset and state is updated to disable intiated by CM.*/
    cmData->extAdvSet[advHandle].timerId = CSR_SCHED_TID_INVALID;
    cmData->extAdvSet[advHandle].state = CM_RPA_STATE_EXT_ADV_DISABLE;
    /* DM queue is locked for next iteration.*/
    CmExtAdvEnableReqSend(CSR_BT_CM_IFACEQUEUE,
                          advHandle,
                          0x00);
}
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */

void CsrBtCmDmExtAdvEnableReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvEnableReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
    /*Bit 0 is set for enabling RPA generation per advertisement set feature */
    if (prim->advHandle < MAX_EXT_ADV_APP &&
       (cmData->extAdvSet[prim->advHandle].extAdvFeatureMask & CM_RPA_GEN_PER_ADV_SET_MASK) == CM_RPA_GEN_PER_ADV_SET_MASK)
    {
        /* Application has disabled the extended advertisement for prim->advHandle.
           In this scenario, timer is cancelled and adv disable request is sent.*/
        if (prim->enable == 0x00 && cmData->dmVar.appHandle != CSR_BT_CM_IFACEQUEUE )
        {
            CsrSchedTimerCancel(cmData->extAdvSet[prim->advHandle].timerId, NULL, NULL);
            dm_ext_adv_enable_req(prim->advHandle, prim->enable, NULL);

           /*If disable from timeout process was queued earlier, that message is cleared from the DM queue and state is reset */
            if (cmData->extAdvSet[prim->advHandle].state == CM_RPA_STATE_EXT_ADV_DISABLE)
            {
                CmDmQueueRemoveMsg(cmData,
                                  CSR_BT_CM_EXT_ADV_ENABLE_REQ,
                                  &(prim->advHandle),
                                  NULL);

                cmData->extAdvSet[prim->advHandle].state = CM_RPA_STATE_NONE;
            }
        }
        /* Extended Advertisement disable is initiated after timeout CmDmExtAdvEnableReqTimeout and the state is updated as set address.
          Set random request procedure is initiated after adv disable.*/
        else if (prim->enable == 0x00 && cmData->dmVar.appHandle == CSR_BT_CM_IFACEQUEUE &&
                 cmData->extAdvSet[prim->advHandle].state != CM_RPA_STATE_EXT_ADV_MULTI_DISABLE)
        {
            cmData->extAdvSet[prim->advHandle].state = CM_RPA_STATE_SET_ADDR;

            dm_ext_adv_enable_req(prim->advHandle, prim->enable, NULL);
            dm_ext_adv_set_random_address_req(prim->advHandle,
                                              CM_EXT_ADV_ADDRESS_GENERATE_RESOLVABLE,
                                              &(cmData->extAdvSet[prim->advHandle].random_addr),
                                              NULL);
        }
        /* Disable received due to duration expiry of respective adv set which was part of multi adv enable.*/
        else if (prim->enable == 0x00 && cmData->dmVar.appHandle == CSR_BT_CM_IFACEQUEUE &&
                 cmData->extAdvSet[prim->advHandle].state == CM_RPA_STATE_EXT_ADV_MULTI_DISABLE)
        {
            CSR_LOG_TEXT_INFO((CsrBtCmLto, 0, "Duration has expired for adv set: 0x%x", prim->advHandle));
            dm_ext_adv_enable_req(prim->advHandle, prim->enable, NULL);
        }
        /* Application has enabled the extended advertisement for this set.
           State is updated to set address and set random request is sent to QBL.*/
        else if (prim->enable == 0x01)
        {
            cmData->extAdvSet[prim->advHandle].state = CM_RPA_STATE_SET_ADDR;

            dm_ext_adv_set_random_address_req(prim->advHandle,
                                              CM_EXT_ADV_ADDRESS_GENERATE_RESOLVABLE,
                                              &(cmData->extAdvSet[prim->advHandle].random_addr),
                                              NULL);
        }
        else
        {
            CSR_LOG_TEXT_INFO((CsrBtCmLto, 0, "Aborting the ext adv with RPA generation process due to invalid operation during ext adv enable for adv set:0x%x.", prim->advHandle));

            CmDmExtAdvEnableReqInitiateAbort(cmData, prim->advHandle);
            dm_ext_adv_enable_req(prim->advHandle, prim->enable, NULL);
        }
    }

    else
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */
    {
        dm_ext_adv_enable_req(prim->advHandle, prim->enable, NULL);
    }
}

void CsrBtCmDmExtAdvReadMaxAdvDataLenReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvReadMaxAdvDataLenReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

    dm_ext_adv_read_max_adv_data_len_req(prim->advHandle, NULL);
}

void CsrBtCmDmExtAdvSetRandomAddrReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvSetRandomAddrReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;

    dm_ext_adv_set_random_address_req(prim->advHandle,
                                      prim->action,
                                      &prim->randomAddr,
                                      NULL);
}

void CsrBtCmDmExtAdvSetsInfoReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvSetsInfoReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;

    dm_ext_adv_sets_info_req(NULL);
}

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
CsrBool CmDmExtAdvMultiEnableSetRandomAddr(cmInstanceData_t *cmData)
{
    static CsrUint8 advSetIdx = 0;
    CmExtAdvMultiEnableReq *multiExtadvEnablePrim = (CmExtAdvMultiEnableReq *)cmData->dmVar.context;

    while (advSetIdx < multiExtadvEnablePrim->numSets)
    {
        CsrUint8 advHandle = multiExtadvEnablePrim->config[advSetIdx].advHandle;

        if ((cmData->extAdvSet[advHandle].extAdvFeatureMask & CM_RPA_GEN_PER_ADV_SET_MASK) == CM_RPA_GEN_PER_ADV_SET_MASK)
        {
            /* CM will initiaite the disable adv process.*/
            cmData->dmVar.appHandle = CSR_BT_CM_IFACEQUEUE;

            dm_ext_adv_enable_req(advHandle, 0x00, NULL);
            dm_ext_adv_set_random_address_req(advHandle,
                                              CM_EXT_ADV_ADDRESS_GENERATE_RESOLVABLE,
                                              &(cmData->extAdvSet[advHandle].random_addr),
                                              NULL);
            advSetIdx++;
            return FALSE;
        }

        else
        {
            advSetIdx++;
            continue;
        }
    }
    advSetIdx = 0;
    return TRUE;
}
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */

void CsrBtCmDmExtAdvMultiEnableReqHandler(cmInstanceData_t *cmData)
{
    CmExtAdvMultiEnableReq *prim = cmData->recvMsgP;

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
    CsrBool isExtAdvRPAEnabled;
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */

    cmData->dmVar.appHandle = prim->appHandle;

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
    /* isExtAdvRPAEnabled checks whether RPA generation per adv set feature is enabled for any set or not.*/
    isExtAdvRPAEnabled = FALSE;

    /* ext adv context stored should be NULL initially.*/
    if (cmData->dmVar.context == NULL)
    {
        /* ext adv context will be freed later.*/
        cmData->dmVar.context = (void *)CsrPmemAlloc(sizeof(CmExtAdvMultiEnableReq));
        CsrMemCpy(cmData->dmVar.context, cmData->recvMsgP, sizeof(CmExtAdvMultiEnableReq));
        /* Application has enabled the multi extended advertisement for these sets.
           State is updated to set address and set random requests are sent to QBL.*/
        if (prim->enable == 0x01)
        {
            CsrUint8 i;

            /* For each adv set, the set random req has been sent to DM.*/
            for (i = 0; i < prim->numSets; i++)
            {
                /*Bit 0 is set for enabling RPA generation per advertisement set feature */
                if (prim->config[i].advHandle < MAX_EXT_ADV_APP &&
                   (cmData->extAdvSet[prim->config[i].advHandle].extAdvFeatureMask & CM_RPA_GEN_PER_ADV_SET_MASK) == CM_RPA_GEN_PER_ADV_SET_MASK)
                {
                    /* RPA generation per adv set feature is not enabled for any adv set which has maxEaEvents as non-zero.*/
                    if (prim->config[i].maxEaEvents == 0x00)
                    {
                        isExtAdvRPAEnabled = TRUE;
                        CmDmExtAdvEnableReqInitiateAbort(cmData, prim->config[i].advHandle);
                        cmData->extAdvSet[prim->config[i].advHandle].isMultiEnable = TRUE;
                        cmData->extAdvSet[prim->config[i].advHandle].state = CM_RPA_STATE_SET_ADDR;
                        /* Storing the duration as milliseconds.*/
                        cmData->extAdvSet[prim->config[i].advHandle].duration = 10 * (CsrUint32)(prim->config[i].duration);

                        /* The expiry of duration will happen before timeout when timeout > duration for the adv set.*/
                        if ((cmData->extAdvSet[prim->config[i].advHandle].duration != 0) &&
                           ((cmData->extAdvSet[prim->config[i].advHandle].timeout) > cmData->extAdvSet[prim->config[i].advHandle].duration))
                        {
                            cmData->extAdvSet[prim->config[i].advHandle].timeout = (CsrUint32)(cmData->extAdvSet[prim->config[i].advHandle].duration);
                        }
                    }

                    else
                    {
                        CSR_LOG_TEXT_ERROR((CsrBtCmLto, 0, "Cancelling RPA generation per adv set feature as maxEaEvents for the adv set 0x%x is given non-zero.", prim->config[i].advHandle));
                        /* If the ext adv process was already running for this adv set, update the duration in cmData.*/
                        if (cmData->extAdvSet[prim->config[i].advHandle].timerId != CSR_SCHED_TID_INVALID ||
                            cmData->extAdvSet[prim->config[i].advHandle].state == CM_RPA_STATE_EXT_ADV_DISABLE)
                        {
                            cmData->extAdvSet[prim->config[i].advHandle].duration = 10 * (CsrUint32)(prim->config[i].duration);
                        }
                    }
                }
            }

            if(isExtAdvRPAEnabled)
            {
                /*. To ensure the set random requests are serialized for respective adv sets, below function is called.*/
                (void)CmDmExtAdvMultiEnableSetRandomAddr(cmData);
            }
        }
        /* Application has disabled the multi extended advertisement for these sets.*/
        else if (prim->enable == 0x00)
        {
            CsrUint8 i;

            for (i = 0; i < prim->numSets; i++)
            {
                if (prim->config[i].advHandle < MAX_EXT_ADV_APP &&
                   (cmData->extAdvSet[prim->config[i].advHandle].extAdvFeatureMask & CM_RPA_GEN_PER_ADV_SET_MASK) == CM_RPA_GEN_PER_ADV_SET_MASK)
                {
                    CmDmExtAdvEnableReqInitiateAbort(cmData, prim->config[i].advHandle);
                }
            }
        }

        else
        {
            CSR_LOG_TEXT_ERROR((CsrBtCmLto, 0, "Multi adv operation could not start for context- appHandle: 0x%x, enabe: 0x%x, numSets: 0x%x", prim->appHandle, prim->enable, prim->numSets));
        }
    }
    /* RPA generation per adv set feature is not enabled for any set or
       the feature is enabled and disable request has been sent.*/
    if (isExtAdvRPAEnabled == FALSE)
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */
    {
        dm_ext_adv_multi_enable_req(prim->enable,
                                    prim->numSets,
                                    (EA_ENABLE_CONFIG_T *) prim->config,
                                    NULL);
    }

}

#ifdef INSTALL_CM_EXT_ADV_SET_PARAM_V2
void CmDmExtAdvSetParamsV2ReqHandler(cmInstanceData_t *cmData)
{
    CmDmExtAdvSetParamsV2Req *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;
    cmData->advHandle = prim->advHandle;

#ifdef INSTALL_RPA_GEN_PER_ADV_SET
    if (prim->advHandle < MAX_EXT_ADV_APP)
    {
        /* Initiated abort to cleanup the RPA gen per adv set feature related parameters in case feature has 
           been disabled after it was enabled previously.*/
        CmDmExtAdvEnableReqInitiateAbort(cmData, prim->advHandle);

        /* Store the feature mask values indicating if specific features are enabled for a given ADV set.*/
        cmData->extAdvSet[prim->advHandle].extAdvFeatureMask = prim->featureMask;
        cmData->extAdvSet[prim->advHandle].isMultiEnable = FALSE;
        cmData->extAdvSet[prim->advHandle].duration = 0x00;
    }
    else
    {
        CsrGeneralException(CsrBtCmLto,
                            0,
                            CSR_BT_CM_PRIM,
                            CM_DM_EXT_ADV_SET_PARAMS_V2_REQ,
                            0,
                            "CmDmExtAdvSetParamsV2ReqHandler invalid adv handle\n");
    }
#endif /* INSTALL_RPA_GEN_PER_ADV_SET */

    dm_ext_adv_set_params_v2_req(prim->advHandle,
                            prim->advEventProperties,
                            prim->primaryAdvIntervalMin,
                            prim->primaryAdvIntervalMax,
                            prim->primaryAdvChannelMap,
                            prim->ownAddrType,
                            &prim->peerAddr,
                            prim->advFilterPolicy,
                            prim->primaryAdvPhy,
                            prim->secondaryAdvMaxSkip,
                            prim->secondaryAdvPhy,
                            prim->advSid,
                            prim->advTxPower,
                            prim->scanReqNotifyEnable,
                            prim->primaryAdvPhyOptions,
                            prim->secondaryAdvPhyOptions,
                            NULL);
}
#endif /* INSTALL_CM_EXT_ADV_SET_PARAM_V2 */


void CmDmExtAdvGetAddressReqHandler(cmInstanceData_t *cmData)
{
    CmDmExtAdvGetAddrReq *prim = cmData->recvMsgP;
    cmData->dmVar.appHandle = prim->appHandle;

    dm_ext_adv_get_addr_req(prim->advHandle, NULL);
}

#endif /* End of CSR_BT_INSTALL_EXTENDED_ADVERTISING */
